import numpy as np
import math
# 评价指标 覆盖率 IGD HV
# 对 不同算法得到的Pareto面的个体进行计算
# 输入：个体的目标值集合 
# 输出：各评价指标的值


# 判断个体1是否支配个体2
# 目标值 输入
def Is_Dominate(Obj1, Obj2):
    print("模块：Evaluate\t方法：Is_Dominate(Obj1, Obj2):")
    # 目标空间维度
    Objective_Dim = len(Obj1)
    # 判断向量
    Flag = [9999] * Objective_Dim
    for i in range(Objective_Dim):
        if Obj1[i] < Obj2[i]:
            Flag[i] = -1    # 目标值小
        elif Obj1[i] == Obj2[i]:
            Flag[i] = 0     # 目标值相等

    # 判断支配的条件如下:
    # 不能全部相等 ---> 不能全0
    # 不能存在个体2目标值比个体1好的   ---> 不能有9999
    return True if sum(Flag) < 0  else False

# 归一化目标值 IGD HV 需要计算距离的 都必须归一化
# 输入：目标值集合 [[,..,],...,[,...,]]
# 输出：归一化后  每个目标值0-1
def Normal_Objective(Obj):
    print("模块：Evaluate\t方法：Normal_Objective(Obj):")
    Obj_Num = len(Obj)    # 个体数量
    if Obj_Num <= 1:      # 个体数量小于1  无需归一化
        return Obj
    Normal_Population_Objection = []    # 种群个体在标准化空间的的目标值
    Object_Dim = []      # 记录每一维
    Object_Dim_Min = []  # 一维的下界每
    Object_Dim_Max = []  # 每一维的上界

    # 存储每一维目标
    for i in range(len(Obj[0])):
        Object_Dim.append([item[i] for item in Obj])    # 每一维目标值 [[,...,],...,]
        if min(Object_Dim[i]) != max(Object_Dim[i]):
            Object_Dim_Min.append(min(Object_Dim[i]))   # 获得ideal点
            Object_Dim_Max.append(max(Object_Dim[i]))
        else:                                           # 在此维上 目标值全部一样 相减为0
            Object_Dim_Min.append(0)                    # 此维不需要归一化 因为距离计算为0
            Object_Dim_Max.append(1)
    # 将解转换到理想点为原点的空间，方便进行标准化操作
    for i in range(Obj_Num):
        Normal_Population_Objection.append(
            ((np.array(Obj[i]) - np.array(Object_Dim_Min)) / 
            (np.array(Object_Dim_Max) - np.array(Object_Dim_Min))).tolist())    
    return Normal_Population_Objection


# 计算标准空间中两个解的距离[,,,],[,,,]
# 输入：两个个体目标值
# 输出：两个个体欧氏距离
def Dist(member1, member2):
    print("模块：Evaluate\t方法：Dist(member1,member2):")
    Euclidean_Distance = 0  # 欧式距离
    for i in range(len(member1)):
        Euclidean_Distance += math.pow((member1[i] - member2[i]), 2)
    Euclidean_Distance = math.sqrt(Euclidean_Distance)
    return Euclidean_Distance



# Pareto 个体数量 N  个体不同目标值相同也算
# 输入：种群 和 Pareto前沿面
# 注意：计算IGD时候 要先去重
def Pareto_Num(Population_Member,Pareto):
    print("模块：Evaluate\t方法：Pareto_Num(Population_Member,Pareto):")
    Temp_Pub = []
    for i in range(len(Pareto)):
        Temp = []
        Temp.append(Population_Member[Pareto[i]].Job_Chromosome.tolist())
        Temp.append(Population_Member[Pareto[i]].Machine_Chromosome.tolist())
        if Temp not in Temp_Pub:
            Temp_Pub.append(Temp)
    # Pareto 面非重复的个体数量 和 个体集合
    return len(Temp_Pub)



# 覆盖率 C(A,B)
# 输入：A B 两个Pareto面   A,B  ---> [[],...,[]]
# 输出：A中个体支配 Pareto B 的个体 / |B|
def Set_Coverage(Pareto_A,Pareto_B):
    print("模块：Evaluate\t方法：Set_Coverage(Pareto_A,Pareto_B):")
    Nodominate_Num = 0     # A 中支配 B的数目
    for i in range(len(Pareto_A)):  # 遍历A中每个个体
        for j in range(len(Pareto_B)):
            if Is_Dominate(Pareto_A[i],Pareto_B[j]):
                Nodominate_Num += 1
                break
    return Nodominate_Num / len(Pareto_B)


# 反转世代距离 IGD
# 输入：A B 两个Pareto面   A,B  ---> [[],...,[]]
# 输出：A B 反转世代距离值
def Inverse_Generational_Distance(Pareto_A,Pareto_B):
    print("模块：Evaluate\t方法：Inverse_Generational_Distance(Pareto_A,Pareto_B):")
    N_Pareto_A_Size = len(Pareto_A)
    N_Pareto_B_Size = len(Pareto_B)
    # 找到合并后的Pareto面 A和B中如果有个体被其他个体支配  那么他一定不在合并Pareto上
    Exclude_A = []  # Pareto A 中被剔除的个体 下标
    Exclude_B = []  # Pareto B 中被剔除的个体 下标
    for i in range(N_Pareto_A_Size):
        for j in range(N_Pareto_B_Size):
            if Is_Dominate(Pareto_A[i],Pareto_B[j]):  # A 中个体支配 B 中个体
                if j not in Exclude_B:
                    Exclude_B.append(j)
            elif Is_Dominate(Pareto_B[j],Pareto_A[i]): 
                if i not in Exclude_A:
                    Exclude_A.append(i)
                break

    # 目标值归一化 不能先进行归一化  必须先计算 合并Pareto
    # 不能分开标准化  因为 Pareto两端个体 无法参与计算
    Combine_Normal = []
    Combine_Normal.extend(Pareto_A)
    Combine_Normal.extend(Pareto_B)

    Combine_Normal = Normal_Objective(Combine_Normal)
    N_Pareto_A = Combine_Normal[0:N_Pareto_A_Size]
    N_Pareto_B = Combine_Normal[N_Pareto_A_Size:N_Pareto_A_Size+N_Pareto_B_Size]
    # 合并Pareto
    True_Pareto = []
    for i in range(N_Pareto_A_Size):
        if i not in Exclude_A:
            True_Pareto.append(N_Pareto_A[i])

    for i in range(N_Pareto_B_Size):
        if i not in Exclude_B:
            True_Pareto.append(N_Pareto_B[i])

    # 算出距离也是一样的 在合并Pareto上的 距离为0  只有不在的才有距离
    # 从合并的Pareto 面上找点
    IGD_A = 0
    IGD_B = 0
    for i in range(len(Exclude_A)):
        Temp_Value = []
        for j in range(len(True_Pareto)):
            Temp_Value.append(Dist(N_Pareto_A[Exclude_A[i]], True_Pareto[j]))
        IGD_A += min(Temp_Value)
    IGD_A /= N_Pareto_A_Size
    for i in range(len(Exclude_B)):
        Temp_Value = []
        for j in range(len(True_Pareto)):
            Temp_Value.append(Dist(N_Pareto_B[Exclude_B[i]],True_Pareto[j]))
        IGD_B += min(Temp_Value)
    IGD_B /= N_Pareto_B_Size
    return IGD_A, IGD_B



# 超体积 HV
# 输入：A B 两个Pareto面   A,B  ---> [[],...,[]]
# 输出：A B 超体积值
# 注意：高维度的超体积很难计算 建议IGD
def Hypervolume(Pareto_A,Pareto_B):
    print("模块：Evaluate\t方法：Hypervolume(Pareto_A,Pareto_B):")
    pass



